﻿using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Colors;
using System.IO;

using NFox.Cad.ExtendMethods;

namespace NFox.Cad.Trans
{
    public class SymbolTable<TTable, TRecord> : IEnumerable<ObjectId>
        where TTable : SymbolTable
        where TRecord : SymbolTableRecord, new()
    {


        internal DBTransaction Trans
        { get; private set; }

        internal Database Database
        {
            get { return Trans.Database; }
        }

        public TTable AcTable
        { get; private set; }


        internal SymbolTable(DBTransaction tr, ObjectId tableId)
        {
            Trans = tr;
            AcTable = Trans.GetObject(tableId, OpenMode.ForRead) as TTable;
        }

        public ObjectId this[string key]
        {
            get
            {
                if (Has(key))
                    return AcTable[key];
                return ObjectId.Null;
            }
        }

        public bool Has(string key)
        {
            return AcTable.Has(key);
        }

        #region Add

        private ObjectId Add(TRecord record)
        {
            using (AcTable.UpgradeOpenAndRun())
            {
                ObjectId id = AcTable.Add(record);
                Trans.Transaction.AddNewlyCreatedDBObject(record, true);
                return id;
            }
        }

        public ObjectId Add(string name, Action<TRecord> action)
        {
            ObjectId id = this[name];
            if (id.IsNull)
            {
                TRecord record = new TRecord();
                record.Name = name;
                id = Add(record);
                if (action != null)
                    action(record);
            }
            return id;
        }

        public TRecord Add(string name)
        {
            TRecord record = GetRecord(name);
            if (record == null)
            {
                record = new TRecord();
                record.Name = name;
                Add(record);
            }
            return record;
        }

        #endregion

        #region Remove

        private void Remove(TRecord record)
        {
            using (record.UpgradeOpenAndRun())
                record.Erase();
        }

        public void Remove(string name)
        {
            TRecord record = GetRecord(name);
            if (record != null)
                Remove(record);
        }

        public void Remove(ObjectId id)
        {
            TRecord record = GetRecord(id);
            if (record != null)
                Remove(record);
        }

        #endregion

        #region GetRecord

        public TRecord GetRecord(ObjectId id, OpenMode openMode)
        {
            if (id.IsNull)
                return null;
            else
                return Trans.GetObject(id, openMode) as TRecord;
        }

        public TRecord GetRecord(ObjectId id)
        {
            return GetRecord(id, OpenMode.ForRead);
        }

        public TRecord GetRecord(string name, OpenMode openMode)
        {
            return GetRecord(this[name], openMode);
        }

        public TRecord GetRecord(string name)
        {
            return GetRecord(name, OpenMode.ForRead);
        }

        public ObjectId GetRecordFrom(SymbolTable<TTable,TRecord> table, string name, bool over)
        {
            ObjectId rid = this[name];
            bool has = rid != ObjectId.Null;

            if ((has && over) || !has)
            {
                ObjectId id = table[name];
                IdMapping idm = new IdMapping();
                table.Database.WblockCloneObjects
                (
                    new ObjectIdCollection { id },
                    AcTable.Id,
                    idm,
                    DuplicateRecordCloning.Replace,
                    false
                );
                rid = idm[id].Value;
            }
            return rid;
        }

        internal ObjectId GetRecordFrom(Func<DBTransaction, SymbolTable<TTable, TRecord>> tableSelector, string fileName, string name, bool over)
        {
            using (var tr = new DBTransaction(fileName))
            {
                return GetRecordFrom(tableSelector(tr), name, over);
            }
        }

        #endregion

        #region IEnumerable<ObjectId> 成员

        public IEnumerable<TRecord> GetRecords()
        {
            return this.Select(id => GetRecord(id));
        }

        public IEnumerator<ObjectId> GetEnumerator()
        {
            foreach (var id in AcTable)
                yield return id;
        }

        #region IEnumerable 成员

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #endregion

        #endregion

    }

    public static class SymbolTableEx
    {

        #region BlockTable

        /// <summary>
        /// 添加块定义
        /// </summary>
        /// <param name="blockName">块定义名</param>
        /// <param name="ents">图元实体集合</param>
        /// <returns>块定义Id</returns>
        public static ObjectId Add(this SymbolTable<BlockTable, BlockTableRecord> table, string blockName, IEnumerable<Entity> ents)
        {
            return table.Add(blockName, btr => table.Trans.AddEntity(btr, ents));
        }

        /// <summary>
        /// 从文件中获取块定义
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="blockName">块定义名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns>块定义Id</returns>
        public static ObjectId GetBlockFrom(this SymbolTable<BlockTable, BlockTableRecord> table, string fileName, string blockName, bool over)
        {
            return
               table.GetRecordFrom(
                    t => t.BlockTable,
                    fileName,
                    blockName,
                    over);
        }

        /// <summary>
        /// 从文件中获取块定义
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns>块定义Id</returns>
        public static ObjectId GetBlockFrom(this SymbolTable<BlockTable, BlockTableRecord> table, string fileName, bool over)
        {

            FileInfo fi = new FileInfo(fileName);
            string blkdefname = fi.Name;
            if (blkdefname.Contains("."))
            {
                blkdefname = blkdefname.Substring(0, blkdefname.LastIndexOf('.'));
            }

            ObjectId id = table[blkdefname];
            bool has = id != ObjectId.Null;
            if ((has && over) || !has)
            {
                Database db = new Database();
                db.ReadDwgFile(fileName, FileShare.Read, true, null);
                id = table.Database.Insert(BlockTableRecord.ModelSpace, blkdefname, db, false);
            }

            return id;
        }

        #endregion

        #region LayerTable

        /// <summary>
        /// 添加图层
        /// </summary>
        /// <param name="layerName">图层名</param>
        /// <param name="color">颜色</param>
        /// <param name="linetypeName">线型名</param>
        /// <param name="lineweight">线宽</param>
        /// <returns></returns>
        public static ObjectId Add(this SymbolTable<LayerTable, LayerTableRecord> table, string layerName, Color color, ObjectId LinetypeId, LineWeight lineweight)
        {

            return
                table.Add(
                    layerName,
                    ltr =>
                    {
                        ltr.Name = layerName;
                        ltr.Color = color;
                        ltr.LinetypeObjectId = LinetypeId;
                        ltr.LineWeight = lineweight;
                    });

        }

        /// <summary>
        /// 从文件中获取图层记录
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="layerName">图层名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns>图层Id</returns>
        public static ObjectId GetLayerFrom(this SymbolTable<LayerTable, LayerTableRecord> table, string fileName, string layerName, bool over)
        {
            return
                table.GetRecordFrom(
                    t => t.LayerTable,
                    fileName,
                    layerName,
                    over);
        }

        #endregion

        #region TextStyleTable

        /// <summary>
        /// 添加文字样式记录
        /// </summary>
        /// <param name="textStyleName">文字样式名</param>
        /// <param name="smallfont">小字体名</param>
        /// <param name="bigfont">大字体名</param>
        /// <param name="xscale">宽度比例</param>
        /// <returns>文字样式Id</returns>
        public static ObjectId Add(this SymbolTable<TextStyleTable, TextStyleTableRecord> table, string textStyleName, string smallfont, string bigfont, double xscale)
        {

            return
                table.Add(
                    textStyleName,
                    tstr =>
                    {
                        tstr.Name = textStyleName;
                        tstr.FileName = smallfont;
                        tstr.BigFontFileName = bigfont;
                        tstr.XScale = xscale;
                    });

        }

        /// <summary>
        /// 添加文字样式记录
        /// </summary>
        /// <param name="textStyleName">文字样式名</param>
        /// <param name="font">字体名</param>
        /// <param name="xscale">宽度比例</param>
        /// <returns>文字样式Id</returns>
        public static ObjectId Add(this SymbolTable<TextStyleTable, TextStyleTableRecord> table, string textStyleName, string font, double xscale)
        {

            return
                table.Add(
                    textStyleName,
                    tstr =>
                    {
                        tstr.Name = textStyleName;
                        tstr.FileName = font;
                        tstr.XScale = xscale;
                    });
        }

        /// <summary>
        /// 从文件中获取文字样式记录
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="key">文字样式名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns>文字样式Id</returns>
        public static ObjectId GetTextStyleFrom(this SymbolTable<TextStyleTable, TextStyleTableRecord> table, string fileName, string textStyleName, bool over)
        {
            return
                table.GetRecordFrom(
                    t => t.TextStyleTable,
                    fileName,
                    textStyleName,
                    over);
        }

        #endregion

        #region DimStyleTable

        private static void SetDimstyleData(this SymbolTable<DimStyleTable, DimStyleTableRecord> table, ObjectId id)
        {
            var dstr = table.GetRecord(id);
            table.Database.SetDimstyleData(dstr);
        }

        /// <summary>
        /// 从文件中获取标注样式记录
        /// </summary>
        /// <param name="target">目标</param>
        /// <param name="source">源</param>
        /// <param name="dimStyleName">标注样式名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns></returns>
        public static ObjectId GetDimStyleFrom(this SymbolTable<DimStyleTable, DimStyleTableRecord> target, SymbolTable<DimStyleTable, DimStyleTableRecord> source, string dimStyleName, bool over)
        {
            ObjectId id =
                target.GetRecordFrom(source, dimStyleName, over);
            target.SetDimstyleData(id);
            target.SetDimstyleData(target.Database.Dimstyle);
            return id;
        }

        /// <summary>
        /// 从文件中获取标注样式记录
        /// </summary>
        /// <param name="table">目标</param>
        /// <param name="fileName">文件名</param>
        /// <param name="dimStyleName">标注样式名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns></returns>
        public static ObjectId GetDimStyleFrom(this SymbolTable<DimStyleTable, DimStyleTableRecord> table, string fileName, string dimStyleName, bool over)
        {
            ObjectId id =
                table.GetRecordFrom(
                    t => t.DimStyleTable,
                    fileName,
                    dimStyleName,
                    over);
            table.SetDimstyleData(id);
            table.SetDimstyleData(table.Database.Dimstyle);
            return id;
        }

        #endregion

        #region LinetypeTable

        /// <summary>
        /// 从文件中获取线型记录
        /// </summary>
        /// <param name="fileName">文件名</param>
        /// <param name="linetypeName">线型名</param>
        /// <param name="over">是否覆盖</param>
        /// <returns>线型Id</returns>
        public static ObjectId GetLinetypeFrom(this SymbolTable<LinetypeTable, LinetypeTableRecord> table, string fileName, string linetypeName, bool over)
        {
            return
                table.GetRecordFrom(
                    t => t.LinetypeTable,
                    fileName,
                    linetypeName,
                    over);
        }

        #endregion

    }

}
